package com.colin.dispatcher;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.v4.app.ActivityCompat;
import android.support.v4.util.SimpleArrayMap;

import static android.support.v4.content.PermissionChecker.checkSelfPermission;

/**
 * Created by colin on 2018/1/18.
 */

public class PermissionUtils {
	// Map of dangerous permissions introduced in later framework versions.
	// Used to conditionally bypass permission-hold checks on older devices.
	private static final SimpleArrayMap<String, Integer> MIN_SDK_PERMISSIONS;

	static {
		MIN_SDK_PERMISSIONS = new SimpleArrayMap<>(8);
		MIN_SDK_PERMISSIONS.put("com.android.voicemail.permission.ADD_VOICEMAIL", 14);
		MIN_SDK_PERMISSIONS.put("android.permission.BODY_SENSORS", 20);
		MIN_SDK_PERMISSIONS.put("android.permission.READ_CALL_LOG", 16);
		MIN_SDK_PERMISSIONS.put("android.permission.READ_EXTERNAL_STORAGE", 16);
		MIN_SDK_PERMISSIONS.put("android.permission.USE_SIP", 9);
		MIN_SDK_PERMISSIONS.put("android.permission.WRITE_CALL_LOG", 16);
		MIN_SDK_PERMISSIONS.put("android.permission.SYSTEM_ALERT_WINDOW", 23);
		MIN_SDK_PERMISSIONS.put("android.permission.WRITE_SETTINGS", 23);
	}

	private static volatile int targetSdkVersion = -1;

	private PermissionUtils() {
	}

	/**
	 * Checks all given permissions have been granted.
	 *
	 * @param grantResults results
	 * @return returns true if all permissions have been granted.
	 */
	public static boolean verifyPermissions(int... grantResults) {
		if (grantResults.length == 0) {
			return false;
		}
		for (int result : grantResults) {
			if (result != PackageManager.PERMISSION_GRANTED) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Returns true if the permission exists in this SDK version
	 *
	 * @param permission permission
	 * @return returns true if the permission exists in this SDK version
	 */
	private static boolean permissionExists(String permission) {
		// Check if the permission could potentially be missing on this device
		Integer minVersion = MIN_SDK_PERMISSIONS.get(permission);
		// If null was returned from the above call, there is no need for a device API level check for the permission;
		// otherwise, we check if its minimum API level requirement is met
		return minVersion == null || Build.VERSION.SDK_INT >= minVersion;
	}

	/**
	 * Returns true if the Activity or Fragment has access to all given permissions.
	 *
	 * @param context     context
	 * @param permissions permission list
	 * @return returns true if the Activity or Fragment has access to all given permissions.
	 */
	public static boolean hasSelfPermissions(Context context, String... permissions) {
		for (String permission : permissions) {
			if (permissionExists(permission) && !hasSelfPermission(context, permission)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Determine context has access to the given permission.
	 *
	 * This is a workaround for RuntimeException of Parcel#readException.
	 * For more detail, check this issue https://github.com/hotchemi/PermissionsDispatcher/issues/107
	 *
	 * @param context context
	 * @param permission permission
	 * @return returns true if context has access to the given permission, false otherwise.
	 * @see #hasSelfPermissions(Context, String...)
	 */
	private static boolean hasSelfPermission(Context context, String permission) {
		try {
			return checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED;
		} catch (RuntimeException t) {
			return false;
		}
	}

	/**
	 * Checks given permissions are needed to show rationale.
	 *
	 * @param activity    activity
	 * @param permissions permission list
	 * @return returns true if one of the permission is needed to show rationale.
	 */
	public static boolean shouldShowRequestPermissionRationale(Activity activity, String... permissions) {
		for (String permission : permissions) {
			if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Get target sdk version.
	 *
	 * @param context context
	 * @return target sdk version
	 */
	@TargetApi(Build.VERSION_CODES.DONUT)
	public static int getTargetSdkVersion(Context context) {
		if (targetSdkVersion != -1) {
			return targetSdkVersion;
		}
		try {
			PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
			targetSdkVersion = packageInfo.applicationInfo.targetSdkVersion;
		} catch (PackageManager.NameNotFoundException ignored) {
		}
		return targetSdkVersion;
	}
}
